package com.t2two.example.common.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.DateConverter;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * @description:
 * @company: 智蓝图汇
 * @author: bruce lan/00001
 * @date: 2022/5/18 11:10
 */
@Slf4j
public class MapBeanUtils {


    public static <T> T mapToBean(Map<String, Object> map, Class<T> class1) {
        T bean = null;
        try {
            bean = class1.newInstance();
            DateConverter dateConverter = new DateConverter(null);
            dateConverter.setPatterns(new String[]{"yyyy-MM-dd", "yyyy-MMM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss.SSS'+00:00'", "EEE MMM dd HH:mm:ss Z yyyy", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"});
            ConvertUtils.register(dateConverter, Date.class);
            BigDecimalConverter bd = new BigDecimalConverter(null);
            ConvertUtils.register(bd, BigDecimal.class);
//            BeanUtils.populate(bean, map);
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String propertyName = entry.getKey();
                Object propertyValue = entry.getValue();
                if (propertyValue != null) {
                    try {
                        BeanUtils.setProperty(bean, propertyName, propertyValue);
                    } catch (Exception e) {
                        // 先打印上面的错误日志
                        log.error("MapBeanUtils_mapToBean_error_info: {}", e.getMessage());
                        // 如果传入的是实体类，则对实体类进行转换
                        try {
                            Class<?> propertyType = bean.getClass().getDeclaredField(propertyName).getType();
                            Object convertedValue = convertValue(propertyValue, propertyType);
                            if (convertedValue != null) {
                                BeanUtils.setProperty(bean, propertyName, convertedValue);
                            }
                        } catch (Exception ex) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            Set<String> fieldKeySet = map.keySet();
            List<String> list = new ArrayList<String>(fieldKeySet);
            Field resetFields = class1.getDeclaredField("resetFields");
            resetFields.setAccessible(true);
            resetFields.set(bean, list);
        } catch (InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
        return bean;
    }

    private static Object convertValue(Object value, Class<?> targetType) {
        if (value instanceof Map) {
            // 如果值是 Map 类型，尝试将其转换为 targetType 对应的 DTO
            try {
                Object dto = targetType.newInstance();
                BeanUtils.populate(dto, (Map<String, Object>) value);
                return dto;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 使用 ConvertUtils 进行类型转换
        Converter converter = ConvertUtils.lookup(targetType);
        if (converter != null) {
            try {
                return converter.convert(targetType, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static Map<?, ?> beanToMap(Object obj) {
        if (null == obj) {
            return null;
        }
        BigDecimalConverter bd = new BigDecimalConverter(BigDecimal.ZERO);
        ConvertUtils.register(bd, BigDecimal.class);
        Map<Object, Object> beanMap = new BeanMap(obj);
        // 去掉属性值为null的字段
        Map<Object, Object> resultMap = new HashMap<>();
        Set<Object> set = beanMap.keySet();
        Iterator<Object> it = set.iterator();
        while (it.hasNext()) {
            Object key = it.next();
            if (beanMap.get(key) != null) {
                resultMap.put(key, beanMap.get(key));
            }
        }
        return resultMap;
    }

    public static Map<String,Object> bean2Map(Object obj){
        if (null == obj) {
            return null;
        }
        BigDecimalConverter bd = new BigDecimalConverter(BigDecimal.ZERO);
        ConvertUtils.register(bd, java.math.BigDecimal.class);
        Map beanMap = new BeanMap(obj);
        // 去掉属性值为null的字段
        Map<String,Object> resultMap = new HashMap();
        Set<String> set = beanMap.keySet();
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            String key = it.next();
            if (beanMap.get(key) != null){
                resultMap.put(key, beanMap.get(key));
            }
        }
        return resultMap;
    }

    public static Map<?, ?> beanToMapWithNull(Object obj) {
        if (null == obj) {
            return null;
        }
        BigDecimalConverter bd = new BigDecimalConverter(BigDecimal.ZERO);
        ConvertUtils.register(bd, BigDecimal.class);
        Map beanMap = new BeanMap(obj);
        return beanMap;
    }


    public static <T> List<T> collectionCopy(Collection source, Class<T> clazz) {
        if (null == source) {
            return Collections.emptyList();
        } else {
            List<T> list = new ArrayList<T>();
            Iterator var3 = source.iterator();

            while (var3.hasNext()) {
                Object o = var3.next();
                list.add(mapToBean((Map<String, Object>) o, clazz));
            }
            return list;
        }
    }

    public static <T> List<T> linkedMapTypeListToObjectList(List<T> lst, Class<T> responseType) {
        lst = JSONObject.parseArray(JSONObject.toJSONString(lst)).toJavaList(responseType);
        return lst;
    }

    /**
     * @param clone:    源对象
     * @param beCloned: 目标对象
     * @return
     * @author : bruce lan/00001
     * @description: 将一个类对象的属性赋值给另一个类对象中的属性
     * @date : 2022-9-5 15:59
     */
    public static Object CloneAttribute(Object clone, Object beCloned) {
        Field[] fieldClone = null;
        Field[] fieldBeCloned = null;
        Map<String, Field> map = new HashMap<String, Field>();
        try {
            Class<?> classClone = clone.getClass();
            Class<?> classBecloned = beCloned.getClass();

            fieldClone = classClone.getDeclaredFields();
            fieldBeCloned = classBecloned.getDeclaredFields();

            for (int t = 0; t < fieldBeCloned.length; t++) {
                map.put(fieldBeCloned[t].getName(), fieldBeCloned[t]);
            }

            for (int i = 0; i < fieldClone.length; i++) {
                String fieldCloneName = fieldClone[i].getName();
                Field fie = map.get(fieldCloneName);
                if (fie != null) {
                    Method method1 = classClone.getMethod(getMethodName(fieldCloneName));
                    Method method2 = classBecloned.getMethod(setMethodName(fieldCloneName), fie.getType());
                    method2.invoke(beCloned, method1.invoke(clone));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fieldClone = null;
            fieldBeCloned = null;
            map.clear();
        }
        return beCloned;
    }

    /**
     * @param source 被复制的对象：源对象
     * @param target 要复制到的对象：目标对象
     * @throw 见catch{}
     * @Description 复制A对象有的属性值赋值给B, 常用与前端页面与后端交互传值，或者传输层见的属性值赋值
     * @Date 2022/3/8 18:47
     */
    public static Object copyBeans(Object source, Object target) {
        //只要有一个对象是空就结束了
        if (source == null || target == null) {
            return null;
        }
        //通过对象.getClass获取源/目标对象的Class文件
        Class sourceClass = source.getClass();
        Class targetClass = target.getClass();
        //获取源对象的所有成员对象
        Field[] sourceClassFields = sourceClass.getDeclaredFields();
        //目标对象的成员数组
        Field[] targetClassFields = targetClass.getDeclaredFields();
        //获取属性名称
        try {
            //遍历源对象的成员
            for (Field field : sourceClassFields) {
                //判断目标对象成员name是否在源对象中存在
                if (!containsName(targetClassFields, field.getName())) {
                    continue;
                }
                //通过源对象的成员field的name值获取目标对象中的成员targetField
                Field targetField = targetClass.getDeclaredField(field.getName());
                //判空
                if (targetField != null) {
                    //如果源对象成员的成员类型和目标成员的类型一致，进行赋值
                    if (field.getType().equals(targetField.getType())) {
                        //解除成员属性的私有限制
                        field.setAccessible(true);
                        //获取source对应成员的属性的值
                        Object value = field.get(source);
                        //恢复成员属性的私有限制
                        field.setAccessible(false);
                        targetField.setAccessible(true);
                        //将value赋值给目标对象的成员
                        targetField.set(target, value);
                        //恢复成员属性的私有限制
                        targetField.setAccessible(false);
                    }
                }
            }

        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return target;

    }

    /**
     * @param fields 数组
     * @param fields 成员name
     * @return true 存在 false 不存在
     * @Title containsName
     * @Description 判断fieldName是否有与Field数组中对象属性相同
     * @Date 2022/3/8 23:19
     */
    protected static boolean containsName(Field[] fields, String fieldName) {
        for (Field field : fields) {
            if (fieldName.equals(field.getName())) {
                return true;
            }
        }
        return false;
    }


    private static String getMethodName(String fieldName) {
        String head = fieldName.substring(0, 1).toUpperCase();
        String tail = fieldName.substring(1);
        return "get" + head + tail;
    }

    private static String setMethodName(String fieldName) {
        String head = fieldName.substring(0, 1).toUpperCase();
        String tail = fieldName.substring(1);
        return "set" + head + tail;
    }
}
